home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / shadow-3.1.4 / usermod.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-05  |  26.0 KB  |  1,252 lines

  1. /*
  2.  * Copyright 1991, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  */
  11.  
  12. #ifndef lint
  13. static    char    sccsid[] = "@(#)usermod.c    3.9    13:22:39    3/9/92";
  14. #endif
  15.  
  16. #include <sys/types.h>
  17. #include <sys/stat.h>
  18. #include <stdio.h>
  19. #include <errno.h>
  20. #include "pwd.h"
  21. #include <grp.h>
  22. #include <ctype.h>
  23. #include <fcntl.h>
  24. #include <time.h>
  25.  
  26. #ifdef    BSD
  27. #include <strings.h>
  28. #else
  29. #include <string.h>
  30. #endif
  31.  
  32. #include "config.h"
  33. #include "shadow.h"
  34. #include "faillog.h"
  35. #include <lastlog.h>
  36.  
  37. #ifdef    USE_SYSLOG
  38. #include <syslog.h>
  39.  
  40. #ifndef    LOG_WARN
  41. #define    LOG_WARN LOG_WARNING
  42. #endif
  43. #endif
  44.  
  45. #ifndef    NGROUPS_MAX
  46. #define    NGROUPS_MAX    64
  47. #endif
  48.  
  49. #define    VALID(s)    (strcspn (s, ":\n") == strlen (s))
  50.  
  51. char    user_name[BUFSIZ];
  52. char    user_newname[BUFSIZ];
  53. uid_t    user_id;
  54. uid_t    user_newid;
  55. gid_t    user_gid;
  56. char    user_comment[BUFSIZ];
  57. char    user_home[BUFSIZ];
  58. char    user_newhome[BUFSIZ];
  59. char    user_shell[BUFSIZ];
  60. long    user_expire;
  61. long    user_inactive;
  62. int    user_ngroups = -1;
  63. gid_t    user_groups[NGROUPS_MAX];
  64. struct    passwd    user_pwd;
  65. struct    spwd    user_spwd;
  66.  
  67. char    *Prog;
  68.  
  69. int    uflg;    /* specify user ID for new account                            */
  70. int    oflg;    /* permit non-unique user ID to be specified with -u          */
  71. int    gflg;    /* primary group ID  for new account                          */
  72. int    Gflg;    /* secondary group set for new account                        */
  73. int    dflg;    /* home directory for new account                             */
  74. int    sflg;    /* shell program for new account                              */
  75. int    cflg;    /* comment (GECOS) field for new account                      */
  76. int    mflg;    /* create user's home directory if it doesn't exist           */
  77. int    fflg;    /* days until account with expired password is locked         */
  78. int    eflg;    /* days after password changed before it becomes expired      */
  79. int    lflg;    /* new user name for user                                     */
  80.  
  81. #ifdef    NDBM
  82. extern    int    pw_dbm_mode;
  83. extern    int    sp_dbm_mode;
  84. extern    int    gr_dbm_mode;
  85. #ifdef    SHADOWGRP
  86. extern    int    sg_dbm_mode;
  87. #endif
  88. #endif
  89. extern    FILE    *fopen();
  90. extern    int    fclose();
  91. extern    char    *malloc();
  92. extern    char    *mktemp();
  93.  
  94. extern    struct    group    *getgrnam();
  95. extern    struct    group    *getgrgid();
  96. extern    struct    group    *gr_next();
  97. extern    struct    group    *gr_locate();
  98. extern    int    gr_lock();
  99. extern    int    gr_unlock();
  100. extern    int    gr_rewind();
  101. extern    int    gr_open();
  102.  
  103. #ifdef    SHADOWGRP
  104. extern    struct    sgrp    *sgr_next();
  105. extern    int    sgr_lock();
  106. extern    int    sgr_unlock();
  107. extern    int    sgr_rewind();
  108. extern    int    sgr_open();
  109. #endif
  110.  
  111. extern    struct    passwd    *getpwnam();
  112. extern    struct    passwd    *pw_next();
  113. extern    struct    passwd    *pw_locate();
  114. extern    int    pw_lock();
  115. extern    int    pw_unlock();
  116. extern    int    pw_rewind();
  117. extern    int    pw_open();
  118.  
  119. extern    int    spw_lock();
  120. extern    int    spw_unlock();
  121. extern    int    spw_open();
  122. extern    struct    spwd    *spw_locate();
  123.  
  124. #define    DAY    (24L*3600L)
  125. #define    WEEK    (7*DAY)
  126.  
  127. #ifdef    ITI_AGING
  128. #define    SCALE    (1)
  129. #else
  130. #define    SCALE    (DAY)
  131. #endif
  132.  
  133. /*
  134.  * days and juldays are used to compute the number of days in the
  135.  * current month, and the cummulative number of days in the preceding
  136.  * months.  they are declared so that january is 1, not 0.
  137.  */
  138.  
  139. static    short    days[13] = { 0,
  140.     31,    28,    31,    30,    31,    30,    /* JAN - JUN */
  141.     31,    31,    30,    31,    30,    31 };    /* JUL - DEC */
  142.  
  143. static    short    juldays[13] = { 0,
  144.     0,    31,    59,    90,    120,    151,    /* JAN - JUN */
  145.     181,    212,    243,    273,    304,    334 };    /* JUL - DEC */
  146.  
  147. #ifdef    NEED_RENAME
  148. /*
  149.  * rename - rename a file to another name
  150.  *
  151.  *    rename is provided for systems which do not include the rename()
  152.  *    system call.
  153.  */
  154.  
  155. int
  156. rename (begin, end)
  157. char    *begin;
  158. char    *end;
  159. {
  160.     struct    stat    s1, s2;
  161.     extern    int    errno;
  162.     int    orig_err = errno;
  163.  
  164.     if (stat (begin, &s1))
  165.         return -1;
  166.  
  167.     if (stat (end, &s2)) {
  168.         errno = orig_err;
  169.     } else {
  170.  
  171.         /*
  172.          * See if this is a cross-device link.  We do this to
  173.          * insure that the link below has a chance of working.
  174.          */
  175.  
  176.         if (s1.st_dev != s2.st_dev) {
  177.             errno = EXDEV;
  178.             return -1;
  179.         }
  180.  
  181.         /*
  182.          * See if we can unlink the existing destination
  183.          * file.  If the unlink works the directory is writable,
  184.          * so there is no need here to figure that out.
  185.          */
  186.  
  187.         if (unlink (end))
  188.             return -1;
  189.     }
  190.  
  191.     /*
  192.      * Now just link the original name to the final name.  If there
  193.      * was no file previously, this link will fail if the target
  194.      * directory isn't writable.  The unlink will fail if the source
  195.      * directory isn't writable, but life stinks ...
  196.      */
  197.  
  198.     if (link (begin, end) || unlink (begin))
  199.         return -1;
  200.  
  201.     return 0;
  202. }
  203. #endif
  204.  
  205. /*
  206.  * strtoday - compute the number of days since 1970.
  207.  *
  208.  * the total number of days prior to the current date is
  209.  * computed.  january 1, 1970 is used as the origin with
  210.  * it having a day number of 0.
  211.  */
  212.  
  213. long
  214. strtoday (str)
  215. char    *str;
  216. {
  217.     char    slop[2];
  218.     int    month;
  219.     int    day;
  220.     int    year;
  221.     long    total;
  222.  
  223.     /*
  224.      * start by separating the month, day and year.  this is
  225.      * a chauvanistic program - it only takes date input in
  226.      * the standard USA format.
  227.      */
  228.  
  229.     if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
  230.         return -1;
  231.  
  232.     /*
  233.      * the month, day of the month, and year are checked for
  234.      * correctness and the year adjusted so it falls between
  235.      * 1970 and 2069.
  236.      */
  237.  
  238.     if (month < 1 || month > 12)
  239.         return -1;
  240.  
  241.     if (day < 1)
  242.         return -1;
  243.  
  244.     if ((month != 2 || (year % 4) != 0) && day > days[month])
  245.         return -1;
  246.     else if ((month == 2 && (year % 4) == 0) && day > 29)
  247.         return -1;
  248.  
  249.     if (year < 0)
  250.         return -1;
  251.     else if (year < 69)
  252.         year += 2000;
  253.     else if (year < 99)
  254.         year += 1900;
  255.  
  256.     if (year < 1970 || year > 2069)
  257.         return -1;
  258.  
  259.     /*
  260.      * the total number of days is the total number of days in all
  261.      * the whole years, plus the number of leap days, plus the
  262.      * number of days in the whole months preceding, plus the number
  263.      * of days so far in the month.
  264.      */
  265.  
  266.     total = (long) ((year - 1970) * 365L) + (((year + 1) - 1970) / 4);
  267.     total += (long) juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
  268.     total += (long) day - 1;
  269.  
  270.     return total;
  271. }
  272.  
  273. /*
  274.  * add_list - add a member to a list of group members
  275.  *
  276.  *    the array of member names is searched for the new member
  277.  *    name, and if not present it is added to a freshly allocated
  278.  *    list of users.
  279.  */
  280.  
  281. char **
  282. add_list (list, member)
  283. char    **list;
  284. char    *member;
  285. {
  286.     int    i;
  287.     char    **tmp;
  288.  
  289.     /*
  290.      * Scan the list for the new name.  Return the original list
  291.      * pointer if it is present.
  292.      */
  293.  
  294.     for (i = 0;list[i] != (char *) 0;i++)
  295.         if (strcmp (list[i], member) == 0)
  296.             return list;
  297.  
  298.     /*
  299.      * Allocate a new list pointer large enough to hold all the
  300.      * old entries, and the new entries as well.
  301.      */
  302.  
  303.     if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
  304.         return 0;
  305.  
  306.     /*
  307.      * Copy the original list to the new list, then append the
  308.      * new member and NULL terminate the result.  This new list
  309.      * is returned to the invoker.
  310.      */
  311.  
  312.     for (i = 0;list[i] != (char *) 0;i++)
  313.         tmp[i] = list[i];
  314.  
  315.     tmp[i++] = strdup (member);
  316.     tmp[i] = (char *) 0;
  317.  
  318.     return tmp;
  319. }
  320.  
  321. /*
  322.  * del_list - delete a member from a list of group members
  323.  *
  324.  *    the array of member names is searched for the old member
  325.  *    name, and if present it is deleted from a freshly allocated
  326.  *    list of users.
  327.  */
  328.  
  329. char **
  330. del_list (list, member)
  331. char    **list;
  332. char    *member;
  333. {
  334.     int    i, j;
  335.     char    **tmp;
  336.  
  337.     /*
  338.      * Scan the list for the new name.  Return the original list
  339.      * pointer if it is present.
  340.      */
  341.  
  342.     for (i = j = 0;list[i] != (char *) 0;i++)
  343.         if (strcmp (list[i], member))
  344.             j++;
  345.  
  346.     if (j == i)
  347.         return list;
  348.  
  349.     /*
  350.      * Allocate a new list pointer large enough to hold all the
  351.      * old entries, and the new entries as well.
  352.      */
  353.  
  354.     if (! (tmp = (char **) malloc ((j + 2) * sizeof member)))
  355.         return 0;
  356.  
  357.     /*
  358.      * Copy the original list to the new list, then append the
  359.      * new member and NULL terminate the result.  This new list
  360.      * is returned to the invoker.
  361.      */
  362.  
  363.     for (i = j = 0;list[i] != (char *) 0;i++)
  364.         if (strcmp (list[i], member))
  365.             tmp[j++] = list[i];
  366.  
  367.     tmp[j] = (char *) 0;
  368.  
  369.     return tmp;
  370. }
  371.  
  372. /*
  373.  * get_groups - convert a list of group names to an array of group IDs
  374.  *
  375.  *    get_groups() takes a comma-separated list of group names and
  376.  *    converts it to an array of group ID values.  Any unknown group
  377.  *    names are reported as errors.
  378.  */
  379.  
  380. int
  381. get_groups (list)
  382. char    *list;
  383. {
  384.     char    *cp;
  385.     struct    group    *grp;
  386.     int    errors = 0;
  387.  
  388.     /*
  389.      * Initialize the list to be empty
  390.      */
  391.  
  392.     user_ngroups = 0;
  393.  
  394.     if (! *list)
  395.         return 0;
  396.  
  397.     /*
  398.      * So long as there is some data to be converted, strip off
  399.      * each name and look it up.  A mix of numerical and string
  400.      * values for group identifiers is permitted.
  401.      */
  402.  
  403.     do {
  404.         /*
  405.          * Strip off a single name from the list
  406.          */
  407.  
  408.         if (cp = strchr (list, ','))
  409.             *cp++ = '\0';
  410.  
  411.         /*
  412.          * Names starting with digits are treated as numerical
  413.          * GID values, otherwise the string is looked up as is.
  414.          */
  415.  
  416.         if (isdigit (*list))
  417.             grp = getgrgid (atoi (list));
  418.         else
  419.             grp = getgrnam (list);
  420.  
  421.         /*
  422.          * There must be a match, either by GID value or by
  423.          * string name.
  424.          */
  425.  
  426.         if (! grp) {
  427.             fprintf (stderr, "%s: unknown group %s\n", Prog, list);
  428.             errors++;
  429.         }
  430.  
  431.         /*
  432.          * Add the GID value from the group file to the user's
  433.          * list of groups.
  434.          */
  435.  
  436.         user_groups[user_ngroups++] = grp->gr_gid;
  437.  
  438.         list = cp;
  439.     } while (list);
  440.  
  441.     /*
  442.      * Any errors in finding group names are fatal
  443.      */
  444.  
  445.     if (errors)
  446.         return -1;
  447.  
  448.     return 0;
  449. }
  450.  
  451. /*
  452.  * usage - display usage message and exit
  453.  */
  454.  
  455. usage ()
  456. {
  457.     fprintf (stderr,
  458.         "usage: %s [-u uid [-o]] [-g group] [-G group,...] \n", Prog);
  459.     fprintf (stderr,
  460.         "\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n");
  461.     fprintf (stderr,
  462.         "\t\t[-f inactive] [-e expire] name\n");
  463.  
  464.     exit (2);
  465. }
  466.  
  467. /*
  468.  * new_pwent - initialize the values in a password file entry
  469.  *
  470.  *    new_pwent() takes all of the values that have been entered and
  471.  *    fills in a (struct passwd) with them.
  472.  */
  473.  
  474. void
  475. new_pwent (pwent)
  476. struct    passwd    *pwent;
  477. {
  478.     if (lflg) {
  479. #ifdef    USE_SYSLOG
  480.         syslog (LOG_INFO, "change user name `%s' to `%s'\n",
  481.             pwent->pw_name, user_newname);
  482. #endif
  483.         pwent->pw_name = strdup (user_newname);
  484.     }
  485.     if (uflg) {
  486. #ifdef    USE_SYSLOG
  487.         syslog (LOG_INFO, "change user `%s' UID from `%d' to `%d'\n",
  488.             pwent->pw_name, pwent->pw_uid, user_newid);
  489. #endif
  490.         pwent->pw_uid = user_newid;
  491.     }
  492.     if (gflg) {
  493. #ifdef    USE_SYSLOG
  494.         syslog (LOG_INFO, "change user `%s' GID from `%d' to `%d'\n",
  495.             pwent->pw_name, pwent->pw_gid, user_gid);
  496. #endif
  497.         pwent->pw_gid = user_gid;
  498.     }
  499.     if (cflg)
  500.         pwent->pw_gecos = strdup (user_comment);
  501.  
  502.     if (dflg) {
  503. #ifdef    USE_SYSLOG
  504.         syslog (LOG_INFO, "change user `%s' home from `%s' to `%s'\n",
  505.             pwent->pw_name, pwent->pw_dir, user_newhome);
  506. #endif
  507.         pwent->pw_dir = strdup (user_newhome);
  508.     }
  509.     if (sflg) {
  510. #ifdef    USE_SYSLOG
  511.         syslog (LOG_INFO, "change user `%s' shell from `%s' to `%s'\n",
  512.             pwent->pw_name, pwent->pw_shell, user_shell);
  513. #endif
  514.         pwent->pw_shell = strdup (user_shell);
  515.     }
  516. }
  517.  
  518. /*
  519.  * new_spent - initialize the values in a shadow password file entry
  520.  *
  521.  *    new_spent() takes all of the values that have been entered and
  522.  *    fills in a (struct spwd) with them.
  523.  */
  524.  
  525. void
  526. new_spent (spent)
  527. struct    spwd    *spent;
  528. {
  529.     if (lflg)
  530.         spent->sp_namp = strdup (user_newname);
  531.  
  532.     if (fflg) {
  533. #ifdef    USE_SYSLOG
  534.         syslog (LOG_INFO, "change user `%s' inactive from `%d' to `%d'\n",
  535.             spent->sp_namp, spent->sp_inact, user_inactive);
  536. #endif
  537.         spent->sp_inact = user_inactive;
  538.     }
  539.     if (eflg) {
  540. #ifdef    USE_SYSLOG
  541.         syslog (LOG_INFO, "change user `%s' expiration from `%d' to `%d'\n",
  542.             spent->sp_namp, spent->sp_expire, user_expire);
  543. #endif
  544.         spent->sp_expire = user_expire;
  545.     }
  546. }
  547.  
  548. /*
  549.  * grp_update - add user to secondary group set
  550.  *
  551.  *    grp_update() takes the secondary group set given in user_groups
  552.  *    and adds the user to each group given by that set.
  553.  */
  554.  
  555. void
  556. grp_update ()
  557. {
  558.     int    i;
  559.     int    is_member;
  560.     int    was_member;
  561.     struct    group    *grp;
  562. #ifdef    SHADOWGRP
  563.     int    was_admin;
  564.     struct    sgrp    *sgrp;
  565. #endif
  566.  
  567.     /*
  568.      * Lock and open the group file.  This will load all of the group
  569.      * entries.
  570.      */
  571.  
  572.     if (! gr_lock ()) {
  573.         fprintf (stderr, "%s: error locking group file\n", Prog);
  574.         exit (1);
  575.     }
  576.     if (! gr_open (O_RDWR)) {
  577.         fprintf (stderr, "%s: error opening group file\n", Prog);
  578.         exit (1);
  579.     }
  580. #ifdef    SHADOWGRP
  581.     if (! sgr_lock ()) {
  582.         fprintf (stderr, "%s: error locking shadow group file\n", Prog);
  583.         exit (1);
  584.     }
  585.     if (! sgr_open (O_RDWR)) {
  586.         fprintf (stderr, "%s: error opening shadow group file\n", Prog);
  587.         exit (1);
  588.     }
  589. #endif
  590.  
  591.     /*
  592.      * Scan through the entire group file looking for the groups that
  593.      * the user is a member of.
  594.      */
  595.  
  596.     for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
  597.  
  598.         /*
  599.          * See if the user specified this group as one of their
  600.          * concurrent groups.
  601.          */
  602.  
  603.         for (i = 0;i < user_ngroups;i++)
  604.             if (grp->gr_gid == user_groups[i])
  605.                 break;
  606.  
  607.         is_member = i == user_ngroups ? -1:i;
  608.  
  609.         for (i = 0;grp->gr_mem[i];i++)
  610.             if (strcmp (user_name, grp->gr_mem[i]) == 0)
  611.                 break;
  612.  
  613.         was_member = grp->gr_mem[i] ? i:-1;
  614.  
  615.         if (is_member == -1 && was_member == -1)
  616.             continue;
  617.  
  618.         if (was_member >= 0 && (! Gflg || is_member >= 0)) {
  619.             if (lflg) {
  620.                 grp->gr_mem = del_list (grp->gr_mem,
  621.                     user_name);
  622.                 grp->gr_mem = add_list (grp->gr_mem,
  623.                     user_newname);
  624. #ifdef    USE_SYSLOG
  625.                 syslog (LOG_INFO,
  626.                     "change `%s' to `%s' in group `%s'\n",
  627.                     user_name, user_newname, grp->gr_name);
  628. #endif
  629.             }
  630.         } else if (was_member >= 0 && Gflg && is_member < 0) {
  631.             grp->gr_mem = del_list (grp->gr_mem, user_name);
  632. #ifdef    USE_SYSLOG
  633.             syslog (LOG_INFO, "delete `%s' from group `%s'\n",
  634.                 user_name, grp->gr_name);
  635. #endif
  636.         } else if (was_member < 0 && Gflg && is_member >= 0) {
  637.             grp->gr_mem = add_list (grp->gr_mem,
  638.                 lflg ? user_newname:user_name);
  639. #ifdef    USE_SYSLOG
  640.             syslog (LOG_INFO, "add `%s' to group `%s'\n",
  641.                 lflg ? user_newname:user_name, grp->gr_name);
  642. #endif
  643.         } else
  644.             continue;
  645.  
  646.         if (! gr_update (grp)) {
  647.             fprintf (stderr, "%s: error adding new group entry\n",
  648.                 Prog);
  649.             exit (1);
  650.         }
  651. #ifdef    NDBM
  652.         /*
  653.          * Update the DBM group file with the new entry as well.
  654.          */
  655.  
  656.         if (! gr_dbm_update (grp)) {
  657.             fprintf (stderr, "%s: cannot add new dbm group entry\n",
  658.                 Prog);
  659.             exit (1);
  660.         }
  661.         endgrent ();
  662. #endif
  663.     }
  664.  
  665. #ifdef    SHADOWGRP
  666.     /*
  667.      * Scan through the entire shadow group file looking for the groups
  668.      * that the user is a member of.
  669.      */
  670.  
  671.     for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
  672.  
  673.         /*
  674.          * See if the user was a member of this group
  675.          */
  676.  
  677.         for (i = 0;sgrp->sg_mem[i];i++)
  678.             if (strcmp (sgrp->sg_mem[i], user_name) == 0)
  679.                 break;
  680.  
  681.         was_member = sgrp->sg_mem[i] ? i:-1;
  682.  
  683.         /*
  684.          * See if the user was an administrator of this group
  685.          */
  686.  
  687.         for (i = 0;sgrp->sg_adm[i];i++)
  688.             if (strcmp (sgrp->sg_adm[i], user_name) == 0)
  689.                 break;
  690.  
  691.         was_admin = sgrp->sg_adm[i] ? i:-1;
  692.  
  693.         /*
  694.          * See if the user specified this group as one of their
  695.          * concurrent groups.
  696.          */
  697.  
  698.         for (i = 0;i < user_ngroups;i++) {
  699.             if (! (grp = gr_locate (sgrp->sg_name)))
  700.                 continue;
  701.  
  702.             if (grp->gr_gid == user_groups[i])
  703.                 break;
  704.         }
  705.         is_member = i == user_ngroups ? -1:i;
  706.  
  707.         if (is_member == -1 && was_admin == -1 && was_member == -1)
  708.             continue;
  709.  
  710.         if (was_admin >= 0 && lflg) {
  711.             sgrp->sg_adm = del_list (sgrp->sg_adm, user_name);
  712.             sgrp->sg_adm = add_list (sgrp->sg_adm, user_newname);
  713. #ifdef    USE_SYSLOG
  714.             syslog (LOG_INFO, "change admin `%s' to `%s' in shadow group `%s'\n",
  715.                 user_name, user_newname, sgrp->sg_name);
  716. #endif
  717.         }
  718.         if (was_member >= 0 && (! Gflg || is_member >= 0)) {
  719.             if (lflg) {
  720.                 sgrp->sg_mem = del_list (sgrp->sg_mem,
  721.                     user_name);
  722.                 sgrp->sg_mem = add_list (sgrp->sg_mem,
  723.                     user_newname);
  724. #ifdef    USE_SYSLOG
  725.                 syslog (LOG_INFO, "change `%s' to `%s' in shadow group `%s'\n",
  726.                     user_name, user_newname, sgrp->sg_name);
  727. #endif
  728.             }
  729.         } else if (was_member >= 0 && Gflg && is_member < 0) {
  730.             sgrp->sg_mem = del_list (sgrp->sg_mem, user_name);
  731. #ifdef    USE_SYSLOG
  732.             syslog (LOG_INFO, "delete `%s' from shadow group `%s'\n",
  733.                 user_name, sgrp->sg_name);
  734. #endif
  735.         } else if (was_member < 0 && Gflg && is_member >= 0) {
  736.             sgrp->sg_mem = add_list (sgrp->sg_mem,
  737.                 lflg ? user_newname:user_name);
  738. #ifdef    USE_SYSLOG
  739.             syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
  740.                 lflg ? user_newname:user_name, grp->gr_name);
  741. #endif
  742.         } else
  743.             continue;
  744.  
  745.         /* 
  746.          * Update the group entry to reflect the changes.
  747.          */
  748.  
  749.         if (! sgr_update (sgrp)) {
  750.             fprintf (stderr, "%s: error adding new group entry\n",
  751.                 Prog);
  752.             exit (1);
  753.         }
  754. #ifdef    NDBM
  755.         /*
  756.          * Update the DBM group file with the new entry as well.
  757.          */
  758.  
  759.         if (! sgr_dbm_update (sgrp)) {
  760.             fprintf (stderr, "%s: cannot add new dbm group entry\n",
  761.                 Prog);
  762.             exit (1);
  763.         }
  764.         endsgent ();
  765. #endif
  766.     }
  767. #endif
  768. }
  769.  
  770. /*
  771.  * check_new_id - verify the new UID for uniqueness
  772.  *
  773.  *    check_new_id() insures that the new UID does not exist already.
  774.  *    It does this by checking that the UID has changed and that there
  775.  *    is no current entry for this user ID.
  776.  */
  777.  
  778. int
  779. check_new_id ()
  780. {
  781.     /*
  782.      * First, the easy stuff.  If the ID can be duplicated, or if
  783.      * the ID didn't really change, just return.  If the ID didn't
  784.      * change, turn off those flags.  No sense doing needless work.
  785.      */
  786.  
  787.     if (oflg)
  788.         return 0;
  789.  
  790.     if (user_id == user_newid) {
  791.         uflg = lflg = 0;
  792.         return 0;
  793.     }
  794.     if (getpwuid (user_newid))
  795.         return -1;
  796.  
  797.     return 0;
  798. }
  799.  
  800. /*
  801.  * process_flags - perform command line argument setting
  802.  *
  803.  *    process_flags() interprets the command line arguments and sets
  804.  *    the values that the user will be created with accordingly.  The
  805.  *    values are checked for sanity.
  806.  */
  807.  
  808. void
  809. process_flags (argc, argv)
  810. int    argc;
  811. char    **argv;
  812. {
  813.     extern    int    optind;
  814.     extern    char    *optarg;
  815.     struct    group    *grp;
  816.     struct    passwd    *pwd;
  817.     struct    spwd    *spwd;
  818.     long    l;
  819.     int    anyflag = 0;
  820.     int    arg;
  821.  
  822.     if (argc == 1 || argv[argc - 1][0] == '-')
  823.         usage ();
  824.  
  825.     if (! (pwd = getpwnam (argv[argc - 1]))) {
  826.         fprintf (stderr, "%s: user %s does not exist\n",
  827.             Prog, argv[argc - 1]);
  828.         exit (6);
  829.     }
  830.     strcpy (user_name, pwd->pw_name);
  831.     user_id = pwd->pw_uid;
  832.     user_gid = pwd->pw_gid;
  833.     strcpy (user_comment, pwd->pw_gecos);
  834.     strcpy (user_home, pwd->pw_dir);
  835.     strcpy (user_shell, pwd->pw_shell);
  836.  
  837.     if (spwd = getspnam (user_name)) {
  838.         user_expire = spwd->sp_expire;
  839.         user_inactive = spwd->sp_inact;
  840.     }
  841.     while ((arg = getopt (argc, argv, "u:og:G:d:s:c:mf:e:l:")) != EOF) {
  842.         switch (arg) {
  843.             case 'c':
  844.                 if (! VALID (optarg)) {
  845.                     fprintf (stderr,
  846.                         "%s: invalid field `%s'\n",
  847.                         Prog, optarg);
  848.                     exit (3);
  849.                 }
  850.                 if (strcmp (optarg, user_comment)) {
  851.                     cflg++;
  852.                     strncpy (user_comment, optarg, BUFSIZ);
  853.                 }
  854.                 break;
  855.             case 'd':
  856.                 if (! VALID (optarg)) {
  857.                     fprintf (stderr,
  858.                         "%s: invalid field `%s'\n",
  859.                         Prog, optarg);
  860.                     exit (3);
  861.                 }
  862.                 dflg++;
  863.                 strncpy (user_newhome, optarg, BUFSIZ);
  864.                 break;
  865.             case 'e':
  866.                 l = strtoday (optarg);
  867. #ifdef    ITI_AGING
  868.                 l *= DAY;
  869. #endif
  870.                 if (l != user_expire) {
  871.                     eflg++;
  872.                     user_expire = l;
  873.                 }
  874.                 break;
  875.             case 'f':
  876.                 if (user_inactive != atoi (optarg)) {
  877.                     fflg++;
  878.                     user_inactive = atoi (optarg);
  879.                 }
  880.                 break;
  881.             case 'g':
  882.                 if (isdigit (optarg[0]))
  883.                     grp = getgrgid (atoi (optarg));
  884.                 else
  885.                     grp = getgrnam (optarg);
  886.  
  887.                 if (! grp) {
  888.                     fprintf (stderr,
  889.                         "%s: unknown group %s\n",
  890.                         Prog, optarg);
  891.                     exit (1);
  892.                 }
  893.                 if (grp->gr_gid != user_gid) {
  894.                     gflg++;
  895.                     user_gid = grp->gr_gid;
  896.                 }
  897.                 break;
  898.             case 'G':
  899.                 Gflg++;
  900.                 if (get_groups (optarg))
  901.                     exit (1);
  902.  
  903.                 break;
  904.             case 'l':
  905.                 if (! VALID (optarg)) {
  906.                     fprintf (stderr,
  907.                         "%s: invalid field `%s'\n",
  908.                         Prog, optarg);
  909.                     exit (3);
  910.                 }
  911.                 if (strcmp (user_name, optarg)) {
  912.                     lflg++;
  913.                     strcpy (user_newname, optarg);
  914.                 }
  915.                 break;
  916.             case 'm':
  917.                 if (! dflg)
  918.                     usage ();
  919.  
  920.                 mflg++;
  921.                 break;
  922.             case 'o':
  923.                 if (! uflg)
  924.                     usage ();
  925.  
  926.                 oflg++;
  927.                 break;
  928.             case 's':
  929.                 if (! VALID (optarg)) {
  930.                     fprintf (stderr,
  931.                         "%s: invalid field `%s'\n",
  932.                         Prog, optarg);
  933.                     exit (3);
  934.                 }
  935.                 if (strcmp (user_shell, optarg)) {
  936.                     strncpy (user_shell, optarg, BUFSIZ);
  937.                     sflg++;
  938.                 }
  939.                 break;
  940.             case 'u':
  941.                 uflg++;
  942.                 user_newid = atoi (optarg);
  943.                 break;
  944.             default:
  945.                 usage ();
  946.         }
  947.         anyflag++;
  948.     }
  949.     if (anyflag == 0) {
  950.         fprintf (stderr, "%s: no flags given\n", Prog);
  951.         exit (1);
  952.     }
  953.     if (optind != argc - 1)
  954.         usage ();
  955.  
  956.     if (dflg && strcmp (user_home, user_newhome) == 0)
  957.         dflg = mflg = 0;
  958.  
  959.     if (uflg && user_id == user_newid)
  960.         uflg = oflg = 0;
  961.  
  962.     if (lflg && getpwnam (user_newname)) {
  963.         fprintf (stderr, "%s: user %s exists\n", Prog, user_newname);
  964.         exit (9);
  965.     }
  966. }
  967.  
  968. /*
  969.  * close_files - close all of the files that were opened
  970.  *
  971.  *    close_files() closes all of the files that were opened for this
  972.  *    new user.  This causes any modified entries to be written out.
  973.  */
  974.  
  975. close_files ()
  976. {
  977.     if (! pw_close ()) {
  978.         fprintf (stderr, "%s: cannot rewrite password file\n", Prog);
  979.         exit (1);
  980.     }
  981.     if (! spw_close ()) {
  982.         fprintf (stderr, "%s: cannot rewrite shadow password file\n",    
  983.             Prog);
  984.         exit (1);
  985.     }
  986.     if (user_ngroups >= 0) {
  987.         if (! gr_close ()) {
  988.             fprintf (stderr, "%s: cannot rewrite group file\n",
  989.                 Prog);
  990.             exit (1);
  991.         }
  992.         (void) gr_unlock ();
  993. #ifdef    SHADOWGRP
  994.         if (! sgr_close ()) {
  995.             fprintf (stderr, "%s: cannot rewrite shadow group file\n",
  996.                 Prog);
  997.             exit (1);
  998.         }
  999.         (void) sgr_unlock ();
  1000. #endif
  1001.     }
  1002.     (void) spw_unlock ();
  1003.     (void) pw_unlock ();
  1004. }
  1005.  
  1006. /*
  1007.  * open_files - lock and open the password files
  1008.  *
  1009.  *    open_files() opens the two password files.
  1010.  */
  1011.  
  1012. open_files ()
  1013. {
  1014.     if (! pw_lock ()) {
  1015.         fprintf (stderr, "%s: unable to lock password file\n", Prog);
  1016.         exit (1);
  1017.     }
  1018.     if (! pw_open (O_RDWR)) {
  1019.         fprintf (stderr, "%s: unable to open password file\n", Prog);
  1020.         exit (1);
  1021.     }
  1022.     if (! spw_lock ()) {
  1023.         fprintf (stderr, "%s: cannot lock shadow password file\n", Prog);
  1024.         exit (1);
  1025.     }
  1026.     if (! spw_open (O_RDWR)) {
  1027.         fprintf (stderr, "%s: cannot open shadow password file\n", Prog);
  1028.         exit (1);
  1029.     }
  1030. }
  1031.  
  1032. /*
  1033.  * usr_update - create the user entries
  1034.  *
  1035.  *    usr_update() creates the password file entries for this user
  1036.  *    and will update the group entries if required.
  1037.  */
  1038.  
  1039. usr_update ()
  1040. {
  1041.     struct    passwd    pwent;
  1042.     struct    spwd    spent;
  1043.     struct    passwd    *pwd;
  1044.     struct    spwd    *spwd;
  1045.  
  1046.     pwd = pw_locate (user_name);
  1047.     pwent = *pwd;
  1048.  
  1049.     spwd = spw_locate (user_name);
  1050.     spent = *spwd;
  1051.  
  1052.     new_pwent (&pwent);
  1053.     new_spent (&spent);
  1054.  
  1055.     if (lflg || uflg || gflg || cflg || dflg || sflg) {
  1056.         if (! pw_update (&pwent)) {
  1057.             fprintf (stderr, "%s: error changing password entry\n",
  1058.                 Prog);
  1059.             exit (1);
  1060.         }
  1061.         if (lflg && ! pw_remove (user_name)) {
  1062.             fprintf (stderr, "%s: error removing password entry\n",
  1063.                 Prog);
  1064.             exit (1);
  1065.         }
  1066. #if defined(DBM) || defined(NDBM)
  1067.         if (access ("/etc/passwd.pag", 0) == 0) {
  1068.             if (! pw_dbm_update (&pwent)) {
  1069.                 fprintf (stderr, "%s: error adding password dbm entry\n",
  1070.                     Prog);
  1071.                 exit (1);
  1072.             }
  1073.             if (lflg && (pwd = getpwnam (user_name)) && ! pw_dbm_remove (pwd)) {
  1074.                 fprintf (stderr, "%s: error removing passwd dbm entry\n",
  1075.                     Prog);
  1076.                 exit (1);
  1077.             }
  1078.             endpwent ();
  1079.         }
  1080. #endif
  1081.     }
  1082.     if (lflg || eflg || fflg) {
  1083.         if (! spw_update (&spent)) {
  1084.             fprintf (stderr, "%s: error adding new shadow password entry\n",
  1085.                 Prog);
  1086.             exit (1);
  1087.         }
  1088.         if (lflg && ! spw_remove (user_name)) {
  1089.             fprintf (stderr, "%s: error removing shadow password entry\n",
  1090.                 Prog);
  1091.             exit (1);
  1092.         }
  1093.     }
  1094. #ifdef    NDBM
  1095.     if (access ("/etc/shadow.pag", 0) == 0) {
  1096.         if (! sp_dbm_update (&spent)) {
  1097.             fprintf (stderr, "%s: error updating shadow passwd dbm entry\n",
  1098.                 Prog);
  1099.             exit (1);
  1100.         }
  1101.         if (lflg && ! sp_dbm_remove (user_name)) {
  1102.             fprintf (stderr, "%s: error removing shadow passwd db entry\n",
  1103.                 Prog);
  1104.             exit (1);
  1105.         }
  1106.         endspent ();
  1107.     }
  1108. #endif
  1109.     if (Gflg || lflg)
  1110.         grp_update ();
  1111. }
  1112.  
  1113. /*
  1114.  * move_home - move the user's home directory
  1115.  *
  1116.  *    move_home() moves the user's home directory to a new location.
  1117.  *    The files will be copied if the directory cannot simply be
  1118.  *    renamed.
  1119.  */
  1120.  
  1121. move_home ()
  1122. {
  1123.     struct    stat    sb;
  1124.  
  1125.     if (mflg && stat (user_home, &sb) == 0) {
  1126.         if (access (user_newhome, 0) == 0) {
  1127.             fprintf (stderr, "%s: directory %s exists\n",
  1128.                 Prog, user_newhome);
  1129.             exit (12);
  1130.         } else if (rename (user_home, user_newhome)) {
  1131.             if (errno == EXDEV) {
  1132.                 if (mkdir (user_newhome, sb.st_mode & 0777)) {
  1133.                     fprintf (stderr,
  1134.                         "%s: can't create %s\n",
  1135.                         Prog, user_newhome);
  1136.                 }
  1137.                 chown (user_newhome, sb.st_uid, sb.st_gid);
  1138.                 if (copy_tree (user_home, user_newhome,
  1139.                         -1, -1) == 0 &&
  1140.                     remove_tree (user_home) == 0 &&
  1141.                         rmdir (user_home) == 0)
  1142.                     return;
  1143.  
  1144.                 remove_tree (user_newhome);
  1145.                 rmdir (user_newhome);
  1146.             }
  1147.             fprintf (stderr,
  1148.                 "%s: cannot rename directory %s to %s\n",
  1149.                 Prog, user_home, user_newhome);
  1150.             exit (12);
  1151.         }
  1152.     }
  1153.     if (uflg || gflg)
  1154.         chown (dflg ? user_newhome:user_home, user_id, user_gid);
  1155. }
  1156.  
  1157. /*
  1158.  * update_files - update the lastlog and faillog files
  1159.  */
  1160.  
  1161. update_files ()
  1162. {
  1163.     struct    lastlog    ll;
  1164.     struct    faillog    fl;
  1165.     int    fd;
  1166.  
  1167.     /*
  1168.      * Relocate the "lastlog" entries for the user.  The old entry
  1169.      * is left alone in case the UID was shared.  It doesn't hurt
  1170.      * anything to just leave it be.
  1171.      */
  1172.  
  1173.     if ((fd = open ("/usr/adm/lastlog", O_RDWR)) != -1) {
  1174.         lseek (fd, (long) user_id * sizeof ll, 0);
  1175.         if (read (fd, &ll, sizeof ll) == sizeof ll) {
  1176.             lseek (fd, (long) user_newid * sizeof ll, 0);
  1177.             write (fd, &ll, sizeof ll);
  1178.         }
  1179.         close (fd);
  1180.     }
  1181.  
  1182.     /*
  1183.      * Relocate the "faillog" entries in the same manner.
  1184.      */
  1185.  
  1186.     if ((fd = open (FAILFILE, O_RDWR)) != -1) {
  1187.         lseek (fd, (long) user_id * sizeof fl, 0);
  1188.         if (read (fd, &fl, sizeof fl) == sizeof fl) {
  1189.             lseek (fd, (long) user_newid * sizeof ll, 0);
  1190.             write (fd, &fl, sizeof fl);
  1191.         }
  1192.         close (fd);
  1193.     }
  1194. }
  1195.  
  1196. /*
  1197.  * main - usermod command
  1198.  */
  1199.  
  1200. main (argc, argv)
  1201. int    argc;
  1202. char    **argv;
  1203. {
  1204.     /*
  1205.      * Get my name so that I can use it to report errors.
  1206.      */
  1207.  
  1208.     if (Prog = strrchr (argv[0], '/'))
  1209.         Prog++;
  1210.     else
  1211.         Prog = argv[0];
  1212.  
  1213. #ifdef    USE_SYSLOG
  1214.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  1215. #endif
  1216.  
  1217.     /*
  1218.      * The open routines for the NDBM files don't use read-write
  1219.      * as the mode, so we have to clue them in.
  1220.      */
  1221.  
  1222. #ifdef    NDBM
  1223.     pw_dbm_mode = O_RDWR;
  1224.     sp_dbm_mode = O_RDWR;
  1225.     gr_dbm_mode = O_RDWR;
  1226. #ifdef    SHADOWGRP
  1227.     sg_dbm_mode = O_RDWR;
  1228. #endif
  1229. #endif
  1230.     process_flags (argc, argv);
  1231.  
  1232.     /*
  1233.      * Do the hard stuff - open the files, change the user entries,
  1234.      * change the home directory, then close and update the files.
  1235.      */
  1236.  
  1237.     open_files ();
  1238.  
  1239.     usr_update ();
  1240.  
  1241.     close_files ();
  1242.  
  1243.     if (mflg)
  1244.         move_home ();
  1245.  
  1246.     if (uflg)
  1247.         update_files ();
  1248.  
  1249.     exit (0);
  1250.     /*NOTREACHED*/
  1251. }
  1252.